Editor.Shape = function () {
	var bbox;
	var draggerResize;
	var deckClose;
	var deckSelect;
	var figure;
	var figureBack;
	var figureText;
	var figureSet;
	var isSelected;
	var impl;
	var shape;
	var uuid;
	var dx;
	var dy;
	var inix;
	var iniy;
	var iniPosx;
	var iniPosy;
	var isActive;
	var isDeleted;
	var minWidth=0;
	var minHeight=0;
	return {
		init: function (i,u) {
			impl = i;
			bbox = new Editor.BBox();
			bbox.init();
			draggerResize = new Editor.DraggerResize();
			draggerResize.init(this);
			deckClose = new Editor.DeckClose();
			deckClose.init(this);
			deckSelect = new Editor.DeckSelect();
			deckSelect.init(this);
			if (u == undefined) {
				uuid = Math.uuid();
			} else {
				uuid = u;
			}
			isActive = false;
			isSelected = false;
			shape = this;
		},
		unselect: function () {
			if (isSelected) {
				deckSelect.remove();
				isSelected = false;
			}
		},
		select: function () {
			deckSelect.draw();
			isSelected = true;
		},
		_remove: function () {
			if (isSelected) impl.unselect();
			figureBack.remove();
			figureText.remove();
			figure.remove();
			delete figureBack;
			delete figureText;
			delete figure;
			deckClose.remove();
			draggerResize.remove();
			isDeleted = true;
		},
		remove: function () {
			shape._remove();
		},
		mouseover: function () {
			if (!Editor.Util.isDragging()) {
				figure.attr(Editor.CONFIG.SHAPE_ATTR_MOUSEOVER);
				draggerResize.mouseover();
				deckClose.mouseover();
			}
		},
		mouseout: function () {
			if (!Editor.Util.isDragging()) {
				figure.attr(impl.getShapeAttr());
				draggerResize.mouseout();
				deckClose.mouseout();
			}
		},
		click: function () {
			impl.toFront();
		},
		toFront: function() {
			figureBack.toFront();
			figureText.toFront();
			figure.toFront();
			draggerResize.toFront();
			deckClose.toFront();
		},
		draw: function () {
			isDeleted = false;
			figureBack = impl.figure();
			figureBack.attr(Editor.CONFIG.SHAPE_ATTR_BACKGROUND);
			figureText = Editor.STATE.CANVAS.text(bbox.getX() + bbox.getWidth() / 2 , bbox.getY() + bbox.getHeight() / 2, impl.getText());
			figureText.attr(impl.getTextAttr());
			figure = impl.figure();
			figureSet = Editor.STATE.CANVAS.set();
			figureSet.push(figureBack);
			figureSet.push(figure);
			figure.mouseover(impl.mouseover);
			figure.mouseout(impl.mouseout);
			figure.mousedown(impl.mousedown);
			figure.click(impl.click);
			draggerResize.draw();
			draggerResize.redraw();
			deckClose.draw();
			deckClose.redraw();
		},
		moveTo: function (newX, newY) {
			var deltaX = newX - bbox.getX();
			var deltaY = newY - bbox.getY();
			if (figure) {
				impl.translate(deltaX,deltaY);
			}
		},
		updateText: function () {
			figureText.attr({
				text: impl.getText(),
				x: bbox.getX() + bbox.getWidth() / 2,
				y: bbox.getY() + bbox.getHeight() / 2
			});
			var tw = figureText.getBBox().width + Editor.CONFIG.SHAPE_TEXT_DELTA_X;
			var th = figureText.getBBox().height + Editor.CONFIG.SHAPE_TEXT_DELTA_Y;
			var dw = tw;
			var dh = th;
			if (Editor.CONFIG.GRID_SNAP) {
				dw = Math.round(dw / Editor.CONFIG.GRID_SIZE) * Editor.CONFIG.GRID_SIZE;
				dh = Math.round(dh / Editor.CONFIG.GRID_SIZE) * Editor.CONFIG.GRID_SIZE;
				if (tw>dw) { 
					dw += Editor.CONFIG.GRID_SIZE;
				}
				if (th>dh) { 
					dh += Editor.CONFIG.GRID_SIZE;
				}
			}
			minWidth = dw;
			minHeight = dh;
			var deltaw = dw-bbox.getWidth();		
			var deltah = dh-bbox.getHeight();		
			if (deltaw<0) {
				deltaw = 0;
			}
			if (deltah<0) {
				deltah = 0;
			}
			impl._resize(deltaw,deltah);
		},
		getMinWidth: function() {
			if (minWidth == 0) {
				impl.updateText;
			}
			return minWidth;
		},
		getMinHeight: function() {
			if (minHeight == 0) {
				impl.updateText();
			}
			return minHeight;
		},
		_resize: function (dw, dh) {
			impl.unselect();
			bbox.setWidth(bbox.getWidth() + dw);
			bbox.setHeight(bbox.getHeight() + dh);
			if (figure) {
				figureSet.attr({
					width: bbox.getWidth(),
					height: bbox.getHeight()
				});
				figureText.attr({
					x: bbox.getX() + bbox.getWidth() / 2,
					y: bbox.getY() + bbox.getHeight() / 2
				});
				draggerResize.translate(dw, dh);
				deckClose.translate(dw, 0);
			}
		},
		resize: function (dw, dh) {
			if (dw!=0 || dh!=0) {
				var cmd = new JS.Command({
					execute: function () {
						impl._resize(dw, dh);
					},
					undo: function () {
						impl._resize(-dw, -dh);
					},
					stack: Editor.STATE.CMDSTACK
				});
				cmd.name = 'resize shape';
				cmd.execute();
			}
		},
		frameResize: function () {
			return Editor.STATE.CANVAS.rect(bbox.getX(), bbox.getY(), bbox.getWidth(), bbox.getHeight(), 0).
			attr(Editor.CONFIG.SHAPE_FRAMERESIZE_ATTR);
		},
		saveIni: function (e) {
			inix = e.clientX;
			iniy = e.clientY;
			iniPosx = bbox.getX();
			iniPosy = bbox.getY();
		},
		mousedown: function (e) {
			if (!Editor.Util.isDragging()) {
				impl.mouseout();
				Editor.Util.hideAllLines();
				dx = e.clientX;
				dy = e.clientY;

				shape.saveIni(e);
				Editor.STATE.DRAGGER = shape;
				isActive = true;

				var arrSelected = Editor.STATE.SELECTED;
				if (arrSelected.length>0) {
					var isSelected = false;
					for(var i=0;i<arrSelected.length;i++) {
						if(arrSelected[i].getUuid() == shape.getUuid()) {
							isSelected = true;
						}
					}
					if (!isSelected) {
						Editor.Util.unselectAll();
					} else {
						for(var i=0;i<arrSelected.length;i++) {
							arrSelected[i].saveIni(e);
						}
					}
				} 
			}
		},
		onmousemove: function (e) {
			var dX = e.clientX - dx;
			var dY = e.clientY - dy;
			dx += dX;
			dy += dY;

			var arrSelected = Editor.STATE.SELECTED;
			if (arrSelected.length>0) {
				for(var i=0;i<arrSelected.length;i++) {
					arrSelected[i].translate(dX,dY);
				}
			} else {
				impl.translate(dX,dY);
			}
		},
		onmouseup: function () {
			var finalX = bbox.getX();
			var finalY = bbox.getY();
			if (Editor.CONFIG.GRID_SNAP) {
				finalX = Math.round(finalX / Editor.CONFIG.GRID_SIZE) * Editor.CONFIG.GRID_SIZE;
				finalY = Math.round(finalY / Editor.CONFIG.GRID_SIZE) * Editor.CONFIG.GRID_SIZE;
			}
			
			var arrSelected = Editor.STATE.SELECTED;
			if (arrSelected.length>0) {
				var traX = finalX - iniPosx ;
				var traY = finalY - iniPosy ;

				var arr = new Array();
				var arrInix = new Array();
				var arrIniy = new Array();
				for(var i=0;i<arrSelected.length;i++) {
					arr.push(arrSelected[i]);
					arrInix.push(arrSelected[i].getIniPosx());
					arrIniy.push(arrSelected[i].getIniPosy());
				}
				var cmd = new JS.Command({
					execute: function () {
						for(var i=0;i<arr.length;i++) {
							arr[i].moveTo(arrInix[i]+traX,arrIniy[i]+traY);
						}
					},
					undo: function () {
						for(var i=0;i<arr.length;i++) {
							arr[i].moveTo(arrInix[i],arrIniy[i]);
						}
					},
					stack: Editor.STATE.CMDSTACK
				});
				cmd.name = 'move shapes';
				cmd.execute();
			} else {
				if (finalX!=iniPosx || finalY!=iniPosy) {
					var iposx = iniPosx;
					var iposy = iniPosy;
					cmd = new JS.Command({
						execute: function () {
							impl.moveTo(finalX,finalY);
						},
						undo: function () {
							impl.moveTo(iposx,iposy);
						}, 
						stack: Editor.STATE.CMDSTACK
					});
					cmd.name = 'move shape';
					cmd.execute();
				} else {
					impl.moveTo(iniPosx,iniPosy);
				}
			}

			Editor.Util.showAllLines();
      impl.onmouseup();
			isActive = false;
			Editor.STATE.DRAGGER = undefined;
		},
		_translate: function (deltaX, deltaY) {
			figureSet.translate(deltaX, deltaY);
			figureText.translate(deltaX, deltaY);
			draggerResize.translate(deltaX, deltaY);
			if (isSelected) {
				deckSelect.translate(deltaX, deltaY);
			}
			deckClose.translate(deltaX, deltaY);
			bbox.setX(bbox.getX()+deltaX);
			bbox.setY(bbox.getY()+deltaY);
		},
		translate: function (deltaX, deltaY) {
			shape._translate(deltaX,deltaY);
		},
		getUuid: function () {
			return uuid;
		},
		isActive: function () {
			return isActive;
		},
		isDeleted: function () {
			return isDeleted;
		},
		setX: function (newX) {
			bbox.setX(newX);
		},
		getX: function () {
			return bbox.getX();
		},
		setY: function (newY) {
			bbox.setY(newX);
		},
		getY: function () {
			return bbox.getY();
		},
		setWidth: function (newWidth) {
			bbox.setWidth(newWidth);
		},
		getWidth: function () {
			return bbox.getWidth();
		},
		setHeight: function (newHeight) {
			bbox.setHeight(newHeight);
		},
		getHeight: function () {
			return bbox.getHeight();
		},
		toString: function () {
			return 'Shape-->(' + 'uuid: ' + uuid + ' ; ' + bbox.toString() + ')'
		},
		getDraggerResize: function () {
			return draggerResize;
		},
		getDeckClose: function () {
			return deckClose;
		},
		getImplementation: function () {
			return impl;
		},
		getIniPosx: function () {
			return iniPosx;
		},
		getIniPosy: function () {
			return iniPosy;
		},
		toObject: function () {
			return {
				'bbox': bbox.toObject(),
				'uuid': uuid
			}
		},
	}
}
